// Fibonacci B
// 
// Application which uses two processes and a shared file to facilitate inter process communication
// 
// Author: F.M.A. Erich
// HAN University of Applied Science
// Arnhem, The Netherlands

using System;
using System.Threading;
using Mono.Unix.Native;
using Mono.Unix;
using System.IO;

namespace Consumer
{
    class MainClass
    {
        public static unsafe void Main(string[] args)
        {
            CircularBuffer cb = new CircularBuffer(25);
            Consumer c = new Consumer(cb);
            c.consume();
        }
    }
    
    class CircularBuffer
    {
        string file = "pr.txt~";
        int fdin;
        public UnixStream us;

        // constructor for Circular Buffer takes argument size, which represents
        // the maximum amount of items which can be stored in this buffer
        public unsafe CircularBuffer(int size)
        {
            if((fdin = Syscall.open(file,  OpenFlags.O_RDWR)) < 0) {
                Console.WriteLine ("fdin={0}", fdin);
                Syscall.perror ("open");
                return;
            }
    
            us = new UnixStream(fdin);
        }
        
        // Retrieves an element from the buffer
        public long getElement()
        {
            byte[] buffer = new byte[16]; // every element is stored as 8 chars
            us.Seek(0, SeekOrigin.Begin);
            us.Read(buffer, 0, sizeof(long)); // read the first element from the stream
            long returnValue = BitConverter.ToInt64(buffer, 0); // convert the read byte array
            if (us.Length-sizeof(long) != 0) // if the stream will not be empty after removing the element we need to shuffle the
				                             // stream data to the left, so the empty space of the first read element is filled again
            {
                byte[] buffer2 = new byte[us.Length-sizeof(long)]; // create a buffer for temporal storage of the other buffer 
				                                                   // minus the just read element
                us.Read(buffer2, 0, (int) us.Length-sizeof(long));
                us.Seek(0, SeekOrigin.Begin);
                us.Write(buffer2, 0, (int) us.Length-sizeof(long)); // write the temporal buffer to the stream
                us.SetLength(us.Length-sizeof(long));               // sets the length of the stream to remove the last element
            }
            else
            {
                us.SetLength(0); // empty the stream
            }
            return returnValue;
        }
    }
    
    class Consumer
    {
        private CircularBuffer cb;

        public Consumer(CircularBuffer cb)
        {
            this.cb = cb;
        }
        
        // Start fetching fibonacci numbers
        public void consume()
        {
            Random RandomClass = new Random();
			Mutex mut = new Mutex();
			Boolean useMutex = false;
			
			try {
				mut = Mutex.OpenExisting("fibobmutex");
				useMutex = true;
			}
			catch (WaitHandleCannotBeOpenedException)
			{
                Syscall.perror("opening mutex");
			}
            
            while(true)
            {
				if (useMutex) mut.WaitOne();
                while (cb.us.Length > 0)
                {
                    Thread.Sleep(RandomClass.Next(0, 1000)); // sleep between 0 and 1 second
                    Console.WriteLine("Fibonacci value: {0}", cb.getElement());
                }
				if (useMutex) mut.ReleaseMutex();
            }
        }
    }
}